-- -------------------------------------------------------------------------
-- Lib/utf8_utils.lua (UTF-8 Byte-Offset Helpers)
-- Pure Lua UTF-8-safe cursor editing utilities.
-- Cursor positions are byte offsets in range [0, #str].
-- -------------------------------------------------------------------------

local utf8_utils = {}

local string_byte = string.byte
local string_len = string.len

-- ASCII (0-127) or multibyte lead byte (192-255)
local function is_utf8_start_byte(byte)
    return (byte and byte < 128) or (byte and byte >= 192)
end

-- Ensures cursor pos is within bounds and not inside a UTF-8 continuation byte.
function utf8_utils.clamp_cursor_pos(str, pos)
    str = str or ""
    local len = string_len(str)
    pos = pos or 0
    if pos < 0 then pos = 0 elseif pos > len then pos = len end

    while pos > 0 and pos < len and not is_utf8_start_byte(string_byte(str, pos + 1)) do
        pos = pos - 1
    end
    return pos
end

-- Returns the byte-offset cursor position before the previous character boundary.
function utf8_utils.prev_char_pos(str, pos)
    str = str or ""
    local len = string_len(str)
    pos = pos or 0
    if pos <= 0 then return 0 end
    if pos > len then pos = len end
    pos = utf8_utils.clamp_cursor_pos(str, pos)

    local p = pos
    repeat
        p = p - 1
    until p <= 0 or is_utf8_start_byte(string_byte(str, p + 1))
    return p
end

-- Returns the byte-offset cursor position after the next character boundary.
function utf8_utils.next_char_pos(str, pos)
    str = str or ""
    local len = string_len(str)
    pos = pos or 0
    if pos >= len then return len end
    if pos < 0 then pos = 0 end
    pos = utf8_utils.clamp_cursor_pos(str, pos)

    local p = pos
    repeat
        p = p + 1
    until p >= len or is_utf8_start_byte(string_byte(str, p + 1))
    return p
end

-- Counts UTF-8 characters (codepoints) by counting lead bytes.
function utf8_utils.char_count(str)
    str = str or ""
    local len = string_len(str)
    local count = 0
    for i = 1, len do
        if is_utf8_start_byte(string_byte(str, i)) then
            count = count + 1
        end
    end
    return count
end

return utf8_utils
